package org.nutz.dao.impl.sql;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.List;

import org.nutz.dao.Condition;
import org.nutz.dao.entity.Entity;
import org.nutz.dao.jdbc.Jdbcs;
import org.nutz.dao.jdbc.ValueAdaptor;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.sql.SqlCallback;
import org.nutz.dao.sql.VarIndex;
import org.nutz.dao.sql.VarSet;
import org.nutz.lang.Lang;
import org.nutz.lang.Mirror;

public class NutSql extends NutStatement implements Sql {

	SqlLiteral literal;

	private VarSet vars;

	private List<VarSet> rows;

	private VarSet lastRow;

	private SqlCallback callback;

	private ValueAdaptor[] adaptors;

	private ValueAdaptor[] clientAdaptors;

	private NutSql() {
		super();
		vars = new SimpleVarSet();
		rows = new LinkedList<VarSet>();
		this.addBatch();
	}

	public NutSql(String sql) {
		this();
		this.literal = new SqlLiteral().valueOf(sql);
		this.setSqlType(this.literal.getType());
		// 根据字面量，构造一个参数的适配器数组
		adaptors = new ValueAdaptor[literal.getParamIndexes().getOrders().size()];
		clientAdaptors = new ValueAdaptor[adaptors.length];
	}

	private NutSql(SqlLiteral literal, SqlCallback callback) {
		this();
		this.literal = literal;
		this.setSqlType(this.literal.getType());
		this.callback = callback;
		// 根据字面量，构造一个参数的适配器数组
		adaptors = new ValueAdaptor[literal.getParamIndexes().getOrders().size()];
		clientAdaptors = new ValueAdaptor[adaptors.length];
	}

	public Sql setEntity(Entity<?> en) {
		return (Sql) super.setEntity(en);
	}

	public ValueAdaptor[] getAdaptors() {
		for (int i = 0; i < adaptors.length; i++) {
			// 采用用户的设定
			if (null != clientAdaptors[i])
				adaptors[i] = clientAdaptors[i];
			// 自动决定
			else if (null == adaptors[i]) {
				// 获得对应参数的名称，以及关联的其他索引
				String name = literal.getParamIndexes().getOrderName(i);
				int[] is = literal.getParamIndexes().getOrderIndex(name);
				// 寻找第一个非 null 的参数
				Object value = null;
				for (VarSet row : rows) {
					value = row.get(name);
					if (null != value)
						break;
				}
				// 找到了，得到适配器，然后循环设置一下
				if (null != value) {
					ValueAdaptor vab = Jdbcs.getAdaptor(Mirror.me(value));
					for (int x : is)
						adaptors[x] = vab;
				}
				// 如果找不到用 Null 适配器
				else {
					adaptors[i] = Jdbcs.Adaptor.asNull;
				}

			}
		}
		return adaptors;
	}

	public void setValueAdaptor(String name, ValueAdaptor adaptor) {
		int[] is = literal.getParamIndexes().getOrderIndex(name);
		if (null != is)
			for (int i : is)
				adaptors[i] = adaptor;
	}

	public Object[][] getParamMatrix() {
		Object[][] re = new Object[rows.size()][adaptors.length];
		int i = 0;
		for (VarSet row : rows) {
			Object[] cols = re[i++];
			for (String name : literal.getParamIndexes().names()) {
				Object value = row.get(name);
				int[] is = literal.getParamIndexes().getOrderIndex(name);
				for (int x : is)
					cols[x] = value;
			}
		}
		return re;
	}

	public String toPreparedStatement() {
		String[] ss = _createSqlElements();

		// 填充参数
		VarIndex vIndex = literal.getParamIndexes();
		for (String name : vIndex.names()) {
			int[] is = vIndex.indexesOf(name);
			for (int i : is)
				ss[i] = "?";

		}
		return Lang.concat(ss).toString();
	}

	/**
	 * 获取语句模板并填充占位符
	 * 
	 * @return 语句模板
	 */
	private String[] _createSqlElements() {
		String[] ss = literal.stack.cloneChain();
		VarIndex vIndex = literal.getVarIndexes();
		VarSet vs = vars;
		for (String name : vIndex.names()) {
			int[] is = vIndex.indexesOf(name);
			Object obj = vs.get(name);
			for (int i : is)
				ss[i] = null == obj ? "" : obj.toString();
		}
		return ss;
	}

	public VarSet vars() {
		return vars;
	}

	public VarSet params() {
		return lastRow;
	}

	public VarIndex varIndex() {
		return literal.getVarIndexes();
	}

	public VarIndex paramIndex() {
		return literal.getParamIndexes();
	}

	public void addBatch() {
		lastRow = new SimpleVarSet();
		rows.add(lastRow);
	}

	public void clearBatch() {
		rows.clear();
		addBatch();
	}

	public Sql setCallback(SqlCallback callback) {
		this.callback = callback;
		return this;
	}

	public Sql setCondition(Condition cnd) {
		this.vars().set("condition", cnd.toSql(this.getEntity()));
		return this;
	}

	public Sql duplicate() {
		return new NutSql(literal, callback);
	}

	public void onBefore(Connection conn) {}

	public void onAfter(Connection conn, ResultSet rs) throws SQLException {
		if (null != callback)
			getContext().setResult(callback.invoke(conn, rs, this));
	}

	@Override
	public String toString() {
		/*
		 * // 语句模板 String[] ss = _createSqlElements();
		 * 
		 * // 填充参数 VarIndex vIndex = literal.getParamIndexes(); VarSet vs =
		 * rows.get(0); for (String name : vIndex.names()) { int[] is =
		 * vIndex.indexesOf(name); String s =
		 * Sqls.formatFieldValue(vs.get(name)).toString(); for (int i : is)
		 * ss[i] = s; }
		 * 
		 * return Lang.concat(ss).toString();
		 */
		return super.toStatement(this.getParamMatrix(), this.toPreparedStatement());
	}

}
